//
//  DPQualityAssurance-Private.m
//  UITestingKit
//
//  Created by Ofri Wolfus on 20/03/07.
//  Copyright 2007 Ofri Wolfus. All rights reserved.
//

#import "DPQualityAssurance-Private.h"
#import "DPObjCRuntime.h"
#import "DPAccessibilityNotificationCenter.h"
#import "DPTestCase.h"	// For DPTestSubject
#import <ExceptionHandling/NSExceptionHandler.h>



@interface DPQualityAssurance (private_stuff_in_main_implementation)
- (id)initWithElement:(DPUIElement *)e;
@end


@implementation DPQualityAssurance (Private)

static NSMutableArray *_testers = nil;
static NSMutableDictionary *_specificTesters = nil;


+ (void)createQAForElement:(DPUIElement *)element {
	NSEnumerator *enumerator = [[element names] objectEnumerator];
	NSString *name;
	
	while ((name = [enumerator nextObject])) {
		Class cls = [_specificTesters objectForKey:name];
		
		if (cls) {
			DPQualityAssurance *qa = [[cls alloc] initWithElement:element];
			
			@synchronized (self) {
				[_testers addObject:qa];
			}
			[qa release];
		}
	}
}

+ (void)elementCreated:(NSNotification *)notif {
	[self createQAForElement:[notif object]];
}

// Alloc and init all available DPQA subclasses
+ (void)setUpTesters {
	Class *classes = dp_copyClassList();
	Class cls, *list = classes;
	
	// At some later point this may be moved to a secondary thread
	@synchronized (self) {
		_testers = [[NSMutableArray alloc] init];
		_specificTesters = [[NSMutableDictionary alloc] init];
		
		for (cls = *list; cls; cls = *(++list)) {
			if (class_isSubclassOfClass(cls, self)) {
				NSString *elemntName = [cls testedElement];
				
				// If our QA is meant to test a specific element,
				// we'll create it only when that element is available.
				// If the element is not yet available, we'll wait for a kAXCreatedNotification
				// notification and then create it as needed. The QA instance is responsible for
				// invoking _takeTesterDown: whenever its element dies.
				if (elemntName) {
					DPUIElement *element = [DPTestSubject anyChildNamed:elemntName];
					
					[_specificTesters setObject:cls
										 forKey:elemntName];
					if (element)
						[self createQAForElement:element];
					else
						[[DPAccessibilityNotificationCenter defaultCenter] addObserver:self
																			  selector:@selector(elementCreated:)
																				  name:(NSString *)kAXCreatedNotification
																				object:DPTestSubject];
				} else {
					DPQualityAssurance *qa = [[cls alloc] init];
					
					[_testers addObject:qa];
					[qa release];
				}
			}
		}
	}
	
	free(classes);
}

+ (void)tearDownTesters {
	@synchronized (self) {
		[_testers release]; _testers = nil;
		[_specificTesters release]; _specificTesters = nil;
	}
}

+ (void)_takeTesterDown:(DPQualityAssurance *)QA {
	@synchronized (self) {
		[_testers removeObject:QA];
	}
}

@end
